(*
  EdgesProcess module
  ------------------------------------
  * Main functions:

  calc_edges string
  processes the edges of a specified image and returns a matrix of colors with edges in black.

*)

(*used for tests*)
let rec wait_key () =
  let time = Sdltimer.get_ticks () in
  let e = Sdlevent.wait_event () in
  match e with
    | Sdlevent.KEYDOWN {Sdlevent.keysym=Sdlkey.KEY_ESCAPE} -> 
        print_string ("Program terminated at "^
                         string_of_int (time / 1000)^" seconds.\n");
        ()
    | _ -> wait_key ()

(*used to display an image (for tests)*)
let show img dst =
  let d = Sdlvideo.display_format img in
  Sdlvideo.blit_surface d dst ();
  Sdlvideo.flip dst
    
(*used to get the negative of an image*)
let inverse img dst =
  let (w,h) = Img.get_dims img in
  for x=0 to w do
    for y=0 to h do
      let (r,g,b) = Sdlvideo.get_pixel_color img x y in
      Sdlvideo.put_pixel_color dst x y (255-r,255-g,255-b);
    done
  done  

(*Gray leveling of original image*)
let color2gray (r,g,b) =
  begin
    let m = ((r+b+g) / 3) in
    (m,m,m)
  end
  
(*Basic method for bluring*)
(*---unused at the moment---*)
let lissageBasic img dst step =
  begin

    let (w,h) = Img.get_dims img in
    
	
    let rm = ref 0 in
    let gm = ref 0 in
    let bm = ref 0 in

    let c = ref 0 in
	
    for x=0 to w-1 do
      for y=0 to h-1 do

	rm := 0; 
	gm := 0;
	bm := 0;
	c := 0;
	for i = x-step to x+step do
	  for j= y-step to y+step do
	    if (i>0 && j >0) then
	      begin
		
		let (cr,cg,cb) = Sdlvideo.get_pixel_color img i j in
		
		rm := cr + !rm;
		gm := cg + !gm;
		bm := cb + !bm;

		incr c;

	      end
	  done;
	done;
	
	rm := !rm / !c;
	gm := !gm / !c;
	bm := !bm / !c;
	
        Sdlvideo.put_pixel_color dst x y (
          !rm,
          !gm,
          !bm
        )
         
      done
    done ;
 
  end

let oilify img dst step =
  begin
    let (w,h) = Img.get_dims img in
    for x=0 to w-1 do
      for y=0 to h-1 do

	let rec add (c,h) = function
	  | [] -> (c,1)::[]
	  | (cc,hh)::l when cc=c -> (cc,hh+1)::l
	  | (cc,hh)::l -> (cc,hh)::(add (c,h) l) in
	
	let list : ((int*int*int)*int) list ref = ref ([]) in
	list := [];
	for i=x-step to x+step do
	  for j=y-step to y+step do
	    if (i>0 && j>0) then
	      begin
		let (r,g,b) = Sdlvideo.get_pixel_color img i j in
		list := add ((r,g,b),1) !list;
	      end
	  done;
	done;
	    
        let rec count x = function
          | [] -> 0
          | (c,n)::l when c=x -> 1 + (count x l)
          | (c,n)::l -> count x l in

        let rec aux = function
          | [] -> []
          | (c,h)::l -> (c,count c !list)::(aux l) in
        
        list := aux !list;
        
        let rec max (c,h) = function
          | [] -> (c,h)
          | (cc,hh)::l when hh > h -> max (cc,hh) l
          | (cc,hh)::l -> max (c,h) l in
        
        let ((r,g,b),h) = max ((1,1,1),0) !list in
        
        Sdlvideo.put_pixel_color dst x y (
          r,
          g,
          b 
        )         
      done
    done ;
  end

(*edge processing function with double derivation*)
let edgesDevSec img dst =
  let (w,h) = Img.get_dims img in
  for x=1 to w-1 do
    for y=1 to h-1 do
      let (rN,gN,bN) = color2gray (Sdlvideo.get_pixel_color img (x) (y-1)) in
      let (rS,gS,bS) = color2gray (Sdlvideo.get_pixel_color img (x) (y+1)) in
      let (rW,gW,bW) = color2gray (Sdlvideo.get_pixel_color img (x-1) (y)) in 
      let (rE,gE,bE) = color2gray (Sdlvideo.get_pixel_color img (x+1) (y)) in
      let (r,g,b) = color2gray (Sdlvideo.get_pixel_color img x y) in
      let color = rN + rS + rE + rW - 4*r in
      if color > 0 then
        Sdlvideo.put_pixel_color dst x y (rN,gN,bN)
      else
        Sdlvideo.put_pixel_color dst x y (0,0,0)
    done
  done
(*basic function of edge processing*)
(*simple derivation*)
let edgesBasic img dst =
  let (w,h) = Img.get_dims img in
  for x=1 to w-1 do
    for y=1 to h-1 do
      let (rN,gN,bN) = color2gray (Sdlvideo.get_pixel_color img (x) (y-1)) in
      let (rS,gS,bS) = color2gray (Sdlvideo.get_pixel_color img (x) (y+1)) in
      let (rW,gW,bW) = color2gray (Sdlvideo.get_pixel_color img (x-1) (y)) in 
      let (rE,gE,bE) = color2gray (Sdlvideo.get_pixel_color img (x+1) (y)) in
      let color = abs (rE-rW) + abs (rN-rS) in
      if color > 15 then
        Sdlvideo.put_pixel_color dst x y (255,255,255)
      else
        Sdlvideo.put_pixel_color dst x y (0,0,0);
    done
  done
  
(*saves BMP to disk*)
let save_img img name =
  let file = name in
  Sdlvideo.save_BMP img file

(*combines the edges and the original image in dst*)
let add_images img1 img2 dst = 
  let (w,h) = Img.get_dims img1 in
  for x = 0 to w do
    for y = 0 to h do
      let (r1,g1,b1) = Sdlvideo.get_pixel_color img1 x y in
      let (r2,g2,b2) = Sdlvideo.get_pixel_color img2 x y in
      if r2 > 0 then
        Sdlvideo.put_pixel_color dst x y (0, 0, 0)
      else
        Sdlvideo.put_pixel_color dst x y (r1,g1,b1)
    done
  done
  
(*creates a matrix of colors from a sdlsurface*)
let create_matrix img = 
  let (w,h) = Img.get_dims img in
  let matrice = Array.make_matrix w h (0,0,0) in
  for x=0 to w-1 do
    for y=0 to h-1 do
      matrice.(x).(y) <- Sdlvideo.get_pixel_color img x y
    done
  done;
  matrice

let unify img fact =
  begin
    let (w,h) = Img.get_dims img in
    for x=0 to w-1 do
      for y=0 to h-1 do
        let (r,g,b) = Sdlvideo.get_pixel_color img x y in
        let (rd,gd,bd) = ((r/fact)*fact,g/fact*fact,b) in
	if (rd < 50 && gd < 50) then
          Sdlvideo.put_pixel_color img x y (0,0,100)
	else 
	  Sdlvideo.put_pixel_color img x y (rd,gd,10)
      done;
    done;
  end

(*Main function*)
(*Processes the image using the previous functions and gives the resulting matrix*)
let calc_edges noise name s  = 
  begin

    Gc.major ();

    let img = Sdlloader.load_image s in
    let (w,h) = Img.get_dims img in
    let dst = Sdlvideo.create_RGB_surface_format img [] w h in

    let dstf = Sdlvideo.create_RGB_surface_format img [] w h in

    if noise > 0 then
      begin
        lissageBasic img dst 5;

        lissageBasic dst img 5;
	
	unify img 50;
	
        oilify img dst noise;
        oilify dst img noise;
 
	lissageBasic img dst 5;
	lissageBasic dst img 5;

	unify img 50;

     end;
    
    edgesDevSec img dst;
    add_images img dst dstf;
    save_img dstf "edges.bmp";
    save_img img name;

   end
